﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Security;
using System.Text;
using Microsoft.VisualBasic.FileIO;
using Microsoft.Win32.SafeHandles;
using SymbolicLinkManageCore;
using SymbolicLinkManageCore.Exceptions;

namespace MkLink
{
    [Flags] public enum LinkType { Invalid = 0, None = 1, JUNCTION = 2, SYMLINK = 4, SYMLINKD = 8, HardLink = 16, SingleFile = 32, NotSupport = None | SingleFile }
    [Flags]
    public enum W32FileAttributes
    {
        None = 0x00000000,
        //=(1) 只读文件。 应用程序可以读取文件，但不能写入或删除该文件。 在目录上不遵循此属性。 有关详细信息，请参阅 无法在 Windows Server 2003、Windows XP、Windows Vista 或 Windows 7 中查看或更改文件夹的只读或系统属性。
        ReadOnly = 0x00000001,

        //=(2) 文件或目录处于隐藏状态。 它不包括在普通目录列表中。
        Hidden = 0x00000002,

        //=(4) 操作系统使用其中的一部分或独占使用的文件或目录。
        System = 0x00000004,

        //=(16) 标识目录的句柄。
        Directory = 0x00000010,

        //=(32) 作为存档文件或目录的文件或目录。 应用程序通常使用此属性来标记要备份或删除的文件。
        Archive = 0x00000020,

        //=(64) 此值保留供系统使用
        Device = 0x00000040,

        //=(128) 未设置其他属性的文件。 此属性仅在单独使用时才有效。
        Normal = 0x00000080,

        //=(256) 用于临时存储的文件。 如果有足够的缓存内存可用，文件系统会避免将数据写回到大容量存储，因为通常情况下，应用程序在句柄关闭后会删除临时文件。 在这种情况下，系统可以完全避免写入数据。 否则，在句柄关闭后写入数据。
        Temporary = 0x00000100,

        //=(512) 是稀疏文件的文件。
        SparseFile = 0x00000200,

        //=(1024) 具有关联的重新分析点的文件或目录，或作为符号链接的文件。
        ReparsePoint = 0x00000400,

        //=(2048) 压缩的文件或目录。 对于文件，将压缩该文件中的所有数据。 对于目录，压缩是新建文件和子目录的默认设置。
        Compressed = 0x00000800,

        //=(4096) 文件的数据不会立即可用。 此属性指示文件数据以物理方式移动到脱机存储。 此属性由远程存储（分层存储管理软件）使用。 应用程序不应随意更改此属性。
        Offline = 0x00001000,

        //=(8192) 内容索引服务不会为文件或目录编制索引。
        NotContentIndexed = 0x00002000,

        //=(16384) 加密的文件或目录。 对于文件，文件中的所有数据流都已加密。 对于目录，加密是新创建的文件和子目录的默认加密。
        Encrypted = 0x00004000,

        //=(32768) 目录或用户数据流配置了完整性 (仅在 ReFS 卷) 上受支持。 它不包括在普通目录列表中。 如果文件已重命名，完整性设置将一直保留。 如果复制了某个文件，则如果源文件或目标目录设置了完整性，则目标文件将具有完整性设置。
        //Windows Server 2008 R2、Windows 7、Windows Server 2008、Windows Vista、Windows Server 2003 和 Windows XP：在Windows Server 2012之前，不支持此标志。
        IntegrityStream = 0x00008000,

        //=(65536) 此值保留供系统使用。
        VIRTUAL = 0x00010000,

        //=(131072) 后台数据完整性扫描程序不读取的用户数据流 (AKA 清理器) 。 在目录上设置时，它仅提供继承。 此标志仅在 存储空间 和 ReFS 卷上受支持。 它不包括在普通目录列表中。
        //Windows Server 2008 R2、Windows 7、Windows Server 2008、Windows Vista、Windows Server 2003 和 Windows XP：在Windows 8和Windows Server 2012之前，不支持此标志。
        NoScrubData = 0x00020000,

        //=(262144) 具有扩展属性的文件或目录。重要： 此常量仅供内部使用。
        EA = 0x00040000,

        //=(524288) 此属性指示用户意图，即即使未主动访问文件或目录，也应在本地保持完全存在。 此属性用于分层存储管理软件。
        Pinned = 0x00080000,

        //=(1048576) 此属性指示，除非主动访问文件或目录，否则不应在本地完全存在。 此属性用于分层存储管理软件。
        UNPinned = 0x00100000,

        //=(262144) 此属性仅出现在目录枚举类 (FILE_DIRECTORY_INFORMATION、FILE_BOTH_DIR_INFORMATION等) 中。 设置此属性时，这意味着文件或目录在本地系统上没有物理表示形式;项是虚拟的。 打开该项目的成本将比平常要高，例如，它会导致至少从远程存储中提取其中一部分。
        Recall_On_Open = 0x00040000,

        //=(4194304) 如果设置了此属性，则意味着文件或目录在本地未完全存在。 对于表示并非所有数据都位于本地存储 (的文件，例如，它可能稀疏，某些数据仍位于远程存储) 中。 对于目录，这意味着某些目录内容正在从另一个位置虚拟化。 读取文件/枚举目录的成本将比平常要高，例如，这会导致至少从远程存储中提取一些文件/目录内容。 只有内核模式调用方可以设置此位。
        //文件系统微型筛选器低于 180000 – 189999高度范围 (FSFilter HSM 加载顺序组) 不得对设置了此属性的文件发出目标缓存读取或写入。 这可能会导致缓存污染和潜在的文件损坏。 有关详细信息，请参阅 处理占位符。
        Recall_On_Data_Access = 0x00400000,
    }
    /// <summary>
    /// 为 NTFS 磁盘提供基于 .NET 实现的目录联接（mklink /J）。
    /// </summary>
    public static unsafe partial class MkLinkTools
    {
        static MkLinkTools()
        {
            string LibDllPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Lib", $"{(Environment.Is64BitProcess ? "x64" : "x86")}", "GetFIleInfo.dll");
            if (!File.Exists(LibDllPath))
            {
                LibDllPath = Path.Combine(Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory.TrimEnd('/', '\\'))!, "Lib", $"{(Environment.Is64BitProcess ? "x64" : "x86")}", "GetFIleInfo.dll");
                if (!File.Exists(LibDllPath))
                    throw new DllNotFoundException($"为找到[GetFIleInfo.dll]搜索路径{LibDllPath}");
            }
            NativeLibrary.Load(LibDllPath);
        }
        [LibraryImport("GetFIleInfo.dll", EntryPoint = "GetFileLinkType", SetLastError = true, StringMarshalling = StringMarshalling.Utf16), SuppressUnmanagedCodeSecurity]
        [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvStdcall) })]
        private static partial LinkType GetFileLinkType(string path, [MarshalAs(UnmanagedType.Bool)] bool IgnorError, int* Error);

        [LibraryImport("GetFIleInfo.dll", EntryPoint = "GetFilelingHardLinks", SetLastError = true, StringMarshalling = StringMarshalling.Utf16), SuppressUnmanagedCodeSecurity]
        [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvStdcall) })]
        private static partial nint GetFilelingHardLinks(string Paht, [MarshalAs(UnmanagedType.Bool)] bool EnableLength);

        [LibraryImport("GetFIleInfo.dll", EntryPoint = "GetFileLinkCount", SetLastError = true, StringMarshalling = StringMarshalling.Utf16), SuppressUnmanagedCodeSecurity]
        [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvStdcall) })]
        private static partial int _GetFileLinkCount(string path, [MarshalAs(UnmanagedType.Bool)] bool IgnorError, int* Error);
        //--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
        //--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
        //[LibraryImport("msvcrt.dll", EntryPoint = "_msize", SetLastError = true)]
        //public static partial uint _msize(byte* handle);


        [StructLayout(LayoutKind.Sequential)]
        private struct BY_HANDLE_FILE_INFORMATION
        {
            public W32FileAttributes FileAttributes;
            public FILETIME CreationTime;
            public FILETIME LastAccessTime;
            public FILETIME LastWriteTime;
            public uint VolumeSerialNumber;
            public uint FileSizeHigh;
            public uint FileSizeLow;
            public uint NumberOfLinks;
            public uint FileIndexHigh;
            public uint FileIndexLow;
        }

        [LibraryImport("kernel32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static partial bool GetFileInformationByHandle(SafeFileHandle handle, BY_HANDLE_FILE_INFORMATION* lpFileInformation);

        [LibraryImport("kernel32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static partial bool CloseHandle(SafeHandle hObject);

        [LibraryImport("kernel32.dll", SetLastError = true, StringMarshalling = StringMarshalling.Utf16)]
        private static partial nint FindFirstFileNameW(string lpFileName, uint dwFlags, ref uint stringLength, byte* fileName);

        [LibraryImport("kernel32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static partial bool FindNextFileNameW(nint hFindStream, ref uint stringLength, byte* fileName);

        [LibraryImport("kernel32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static partial bool FindClose(nint fFindHandle);

        [LibraryImport("kernel32.dll", StringMarshalling = StringMarshalling.Utf16)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static partial bool GetVolumePathNameW(string lpszFileName, byte* lpszVolumePathName, uint cchBufferLength);


        /// <summary>
        /// The file or directory is not a reparse point.
        /// </summary>
        private const int ERROR_NOT_A_REPARSE_POINT = 4390;

        /// <summary>
        /// The reparse point attribute cannot be set because it conflicts with an existing attribute.
        /// </summary>
        private const int ERROR_REPARSE_ATTRIBUTE_CONFLICT = 4391;

        /// <summary>
        /// The data present in the reparse point buffer is invalid.
        /// </summary>
        private const int ERROR_INVALID_REPARSE_DATA = 4392;

        /// <summary>
        /// The tag present in the reparse point buffer is invalid.
        /// </summary>
        private const int ERROR_REPARSE_TAG_INVALID = 4393;

        /// <summary>
        /// There is a mismatch between the tag specified in the request and the tag present in the reparse point.
        /// </summary>
        private const int ERROR_REPARSE_TAG_MISMATCH = 4394;

        /// <summary>
        /// Command to set the reparse point data block.
        /// </summary>
        private const int FSCTL_SET_REPARSE_POINT = 0x000900A4;

        /// <summary>
        /// Command to get the reparse point data block.
        /// </summary>
        private const int FSCTL_GET_REPARSE_POINT = 0x000900A8;

        /// <summary>
        /// Command to delete the reparse point data base.
        /// </summary>
        private const int FSCTL_DELETE_REPARSE_POINT = 0x000900AC;

        /// <summary>
        /// Reparse point tag used to identify mount points and junction points.
        /// </summary>
        private const uint IO_REPARSE_TAG_MOUNT_POINT = 0xA0000003;

        /// <summary>
        /// Reparse point tag used to identify mount points and SYMLINKOrSYMLINKD points.
        /// </summary>
        private const uint IO_REPARSE_TAG_SYMLINK = 0xA000000C;

        /// <summary>
        /// This prefix indicates to NTFS that the path is to be treated as a non-interpreted
        /// path in the virtual file system.
        /// </summary>
        private const string NonInterpretedPathPrefix = @"\??\";

        [Flags] private enum EFileAccess : uint { GenericRead = 0x80000000, GenericWrite = 0x40000000, GenericExecute = 0x20000000, GenericAll = 0x10000000, }

        [Flags] private enum EFileShare : uint { None = 0x00000000, Read = 0x00000001, Write = 0x00000002, Delete = 0x00000004, }

        private enum ECreationDisposition : uint { New = 1, CreateAlways = 2, OpenExisting = 3, OpenAlways = 4, TruncateExisting = 5, }

        [Flags] private enum SYMBOLIC_LINK_FLAG : uint { File = 0x0, DIRECTORY = 0x1, ALLOW_UNPRIVILEGED_CREATE = 0x2 }

        [Flags]
        private enum EFileAttributes : uint
        {
            Readonly = 0x00000001,
            Hidden = 0x00000002,
            System = 0x00000004,
            Directory = 0x00000010,
            Archive = 0x00000020,
            Device = 0x00000040,
            Normal = 0x00000080,
            Temporary = 0x00000100,
            SparseFile = 0x00000200,
            ReparsePoint = 0x00000400,
            Compressed = 0x00000800,
            Offline = 0x00001000,
            NotContentIndexed = 0x00002000,
            Encrypted = 0x00004000,
            Write_Through = 0x80000000,
            Overlapped = 0x40000000,
            NoBuffering = 0x20000000,
            RandomAccess = 0x10000000,
            SequentialScan = 0x08000000,
            DeleteOnClose = 0x04000000,
            BackupSemantics = 0x02000000,
            PosixSemantics = 0x01000000,
            OpenReparsePoint = 0x00200000,
            OpenNoRecall = 0x00100000,
            FirstPipeInstance = 0x00080000
        }

        [StructLayout(LayoutKind.Sequential)]
        private struct REPARSE_DATA_BUFFER
        {
            /// <summary>
            /// Reparse point tag. Must be a Microsoft reparse point tag.
            /// </summary>
            public uint ReparseTag;

            /// <summary>
            /// Size, in bytes, of the data after the Reserved member. This can be calculated by:
            /// (4 * sizeof(ushort)) + SubstituteNameLength + PrintNameLength + 
            /// (namesAreNullTerminated ? 2 * sizeof(char) : 0);
            /// </summary>
            public ushort ReparseDataLength;

            /// <summary>
            /// Reserved; do not use. 
            /// </summary>
            public ushort Reserved;

            /// <summary>
            /// Offset, in bytes, of the substitute name string in the PathBuffer array.
            /// </summary>
            public ushort SubstituteNameOffset;

            /// <summary>
            /// Length, in bytes, of the substitute name string. If this string is null-terminated,
            /// SubstituteNameLength does not include space for the null character.
            /// </summary>
            public ushort SubstituteNameLength;

            /// <summary>
            /// Offset, in bytes, of the print name string in the PathBuffer array.
            /// </summary>
            public ushort PrintNameOffset;

            /// <summary>
            /// Length, in bytes, of the print name string. If this string is null-terminated,
            /// PrintNameLength does not include space for the null character. 
            /// </summary>
            public ushort PrintNameLength;

            /// <summary>
            /// A buffer containing the unicode-encoded path string. The path string contains
            /// the substitute name string and print name string.
            /// </summary>
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x3FF0)]
            public byte[] PathBuffer;
        }
        [LibraryImport("Kernel32", StringMarshalling = StringMarshalling.Utf16)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static partial bool CreateHardLink(string linkName, string sourceName, nint attribute);

        [LibraryImport("Kernel32", StringMarshalling = StringMarshalling.Utf16)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static partial bool CreateSymbolicLink(string linkName, string sourceName, SYMBOLIC_LINK_FLAG attribute);

        [LibraryImport("kernel32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static partial bool DeviceIoControl(nint hDevice, uint dwIoControlCode, nint InBuffer, int nInBufferSize, nint OutBuffer, int nOutBufferSize, out int pBytesReturned, nint lpOverlapped);

        private static void ThrowLastWin32Error(string message) => throw new IOException(message, Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error()));


        [LibraryImport("kernel32.dll", SetLastError = true, StringMarshalling = StringMarshalling.Utf16)]
        private static partial nint CreateFileW(string lpFileName, EFileAccess dwDesiredAccess, EFileShare dwShareMode, nint lpSecurityAttributes, ECreationDisposition dwCreationDisposition, EFileAttributes dwFlagsAndAttributes, nint hTemplateFile);
        private static SafeFileHandle CreateSafeFileHandel(string reparsePoint, EFileAccess accessMode, bool IgnorExceptions = false)
        {
            SafeFileHandle reparsePointHandle = new SafeFileHandle(CreateFileW(reparsePoint, accessMode,
                EFileShare.Read,
                nint.Zero, ECreationDisposition.OpenExisting,
                EFileAttributes.BackupSemantics | EFileAttributes.OpenReparsePoint | EFileAttributes.Archive,
                nint.Zero), true);

            if (!IgnorExceptions && Marshal.GetLastWin32Error() != 0)
                ThrowLastWin32Error($"打开路径失败:[{reparsePoint}]");

            return reparsePointHandle;
        }



        private static void CreateHardLink(string LinkPoint, string LinkTargetPath)
        {
            if (!IsLocal(LinkPoint))
                throw new DriveNotFoundException($"路径[{LinkPoint}]不是一个本地路径");
            if (!IsLocal(LinkTargetPath))
                throw new DriveNotFoundException($"路径[{LinkTargetPath}]不是一个本地路径");
            if (!Path.GetPathRoot(LinkPoint)!.Equals(Path.GetPathRoot(LinkTargetPath), StringComparison.OrdinalIgnoreCase))
                throw new NotSupportedException($"连接点与目标点不在同一个逻辑磁盘下\r\n连接点盘符[{Path.GetPathRoot(LinkPoint)}],目标盘符[{Path.GetPathRoot(LinkTargetPath)}]\r\n连接点[{LinkPoint}],目标[{LinkTargetPath}]");
            CreateHardLink(LinkPoint, LinkTargetPath, nint.Zero);
        }
        private static void CreateJunctionLink(string JunctionPoint, string LinkTargetDirectory)
        {
            Directory.CreateDirectory(JunctionPoint);

            using (SafeFileHandle handle = CreateSafeFileHandel(JunctionPoint, EFileAccess.GenericWrite))
            {
                var targetDirectoryBytes = Encoding.Unicode.GetBytes(NonInterpretedPathPrefix + Path.GetFullPath(LinkTargetDirectory));

                REPARSE_DATA_BUFFER reparseDataBuffer = new REPARSE_DATA_BUFFER
                {
                    ReparseTag = IO_REPARSE_TAG_MOUNT_POINT,
                    ReparseDataLength = (ushort)(targetDirectoryBytes.Length + 12),
                    SubstituteNameOffset = 0,
                    SubstituteNameLength = (ushort)targetDirectoryBytes.Length,
                    PrintNameOffset = (ushort)(targetDirectoryBytes.Length + 2),
                    PrintNameLength = 0,
                    PathBuffer = new byte[0x3ff0]
                };
                Array.Copy(targetDirectoryBytes, reparseDataBuffer.PathBuffer, targetDirectoryBytes.Length);

                var inBufferSize = Marshal.SizeOf(reparseDataBuffer);
                var inBuffer = Marshal.AllocHGlobal(inBufferSize);

                try
                {
                    Marshal.StructureToPtr(reparseDataBuffer, inBuffer, false);
                    var result = DeviceIoControl(handle.DangerousGetHandle(), FSCTL_SET_REPARSE_POINT, inBuffer, targetDirectoryBytes.Length + 20, nint.Zero, 0, out var bytesReturned, nint.Zero);
                    if (!result)
                        ThrowLastWin32Error("无法创建目录联接。");
                }
                finally
                {
                    Marshal.FreeHGlobal(inBuffer);
                }
            }
        }
        

        
        private static uint GetFileLinkCount_Net(SafeFileHandle handle)
        {
            BY_HANDLE_FILE_INFORMATION fileInfo;
            if (GetFileInformationByHandle(handle, &fileInfo))
                return fileInfo.NumberOfLinks;
            return 0;
        }
        public static uint GetFileLinkCount_Net(string filepath)
        {
            using (SafeFileHandle handle = CreateSafeFileHandel(filepath, EFileAccess.GenericRead))
            {
                BY_HANDLE_FILE_INFORMATION fileInfo;
                if (GetFileInformationByHandle(handle, &fileInfo))
                    return fileInfo.NumberOfLinks;
                //CloseHandle(handle);
                return 0;
            }
        }
        public static uint GetFileLinkCount(string FilePath)
        {
            int err;
            int result = _GetFileLinkCount(FilePath, false, &err);
            if (result < 1)
                return 0;
            return (uint)result;
        }


        public static string[] GetFileHardLinks_Net(string filepath)
        {
            List<string> result = new();
            uint stringLength = 256;
            byte* sb = stackalloc byte[256];
            GetVolumePathNameW(filepath, sb, stringLength);
            string volume = Marshal.PtrToStringUni((nint)sb) ?? string.Empty;
            stringLength = 256;
            nint findHandle = FindFirstFileNameW(filepath, 0, ref stringLength, sb);
            if (((int)findHandle) != -1)
            {
                string CachePath;
                do
                {
                    CachePath = Marshal.PtrToStringUni((nint)sb) ?? string.Empty;
                    result.Add(Path.Combine(volume, CachePath.TrimStart('/', '\\')));
                    stringLength = 256;
                } while (FindNextFileNameW(findHandle, ref stringLength, sb));
                FindClose(findHandle);
                return result.ToArray();
            }
            return [];
        }
        public static string[] GetFileHardLinks_Asterisk(string FilePath)
        {
            nint resultPtr = nint.Zero;
            try
            {
                resultPtr = GetFilelingHardLinks(FilePath, false);
                string? result = Marshal.PtrToStringUni(resultPtr);
                if (string.IsNullOrEmpty(result))
                    return [];
                return result.Trim().Split('*', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
            }
            finally
            {
                if (resultPtr != nint.Zero)
                    Marshal.FreeHGlobal(resultPtr);
            }
        }
        const int UInt32Size = sizeof(uint);
        const int charSize = sizeof(char);
        private static string[] SplitUnmanagedstring(byte* Cache)
        {
            List<string> list = new(10);
            byte* ReadLocation = Cache;

            int Len = *ReadLocation - UInt32Size;
            ReadLocation += UInt32Size;
            
            while(true)
            {
                list.Add(new string((char*)ReadLocation, 0, Len / charSize));
                ReadLocation += Len;
                Len = *ReadLocation - UInt32Size;
                if(Len<1) break;
                ReadLocation += UInt32Size;
            }
            return list.ToArray();
        }
        public static string[] GetFileHardLinks_Length(string FilePath)
        {
            nint resultPtr = nint.Zero;
            try
            {
                resultPtr = GetFilelingHardLinks(FilePath, true);
                if (resultPtr == nint.Zero)
                    return [];
                return SplitUnmanagedstring((byte*)resultPtr);
            }
            finally
            {
                if (resultPtr != nint.Zero)
                    Marshal.FreeHGlobal(resultPtr);
            }
        }


        public static readonly string[] LocalPathRoots = DriveInfo.GetDrives().Where(p => p.DriveType == DriveType.Fixed).Select(p => p.Name).ToArray();
        public static bool IsLocal(string ItemPath) => LocalPathRoots.Contains(Path.GetPathRoot(ItemPath), StringComparer.OrdinalIgnoreCase);


        private static BY_HANDLE_FILE_INFORMATION? GetFileInfo(SafeFileHandle handle)
        {
            BY_HANDLE_FILE_INFORMATION fileInfo;
            if (GetFileInformationByHandle(handle, &fileInfo))
                return fileInfo;
            return null;
        }
        /// <summary>
        /// 获取指定连接点路径的连接类型
        /// </summary>
        /// <param name="LinkPointPath">连接点路径</param>
        /// <param name="CheckExists">检查路径是否存在--关闭时必须对FileMode参数赋值</param>
        /// <param name="FileMode">路径的模式，如果路径是一个文件路径则为True 否则为false</param>
        /// <returns>连接点类型</returns>
        /// <exception cref="ArgumentException">参数错误异常</exception>
        public static LinkType GetLinkType_Net(string LinkPointPath, bool CheckExists = true, bool IgnorExceptions = false)
        {
            if (CheckExists && !Path.Exists(LinkPointPath))
                return LinkType.None;

            using (SafeFileHandle handle = CreateSafeFileHandel(LinkPointPath, EFileAccess.GenericRead, IgnorExceptions))
            {
                if (handle.IsInvalid)
                    return LinkType.None;
                BY_HANDLE_FILE_INFORMATION? fileInfo = GetFileInfo(handle);
                if (fileInfo == null)
                    return LinkType.None;

                if ((fileInfo.Value.FileAttributes & W32FileAttributes.ReparsePoint) > 0)
                {
                    int outBufferSize = Marshal.SizeOf(typeof(REPARSE_DATA_BUFFER));
                    nint outBuffer = Marshal.AllocHGlobal(outBufferSize);
                    try
                    {
                        if (!DeviceIoControl(handle.DangerousGetHandle(), FSCTL_GET_REPARSE_POINT, nint.Zero, 0, outBuffer, outBufferSize, out var bytesReturned, nint.Zero))
                        {
                            int error = Marshal.GetLastWin32Error();
                            if (error == ERROR_NOT_A_REPARSE_POINT)
                            {
                                return LinkType.None;
                            }
                            ThrowLastWin32Error("无法获取Link点信息");
                        }
                        REPARSE_DATA_BUFFER reparseDataBuffer = (REPARSE_DATA_BUFFER)Marshal.PtrToStructure(outBuffer, typeof(REPARSE_DATA_BUFFER))!;
                        if (reparseDataBuffer.ReparseTag == IO_REPARSE_TAG_MOUNT_POINT)
                            return LinkType.JUNCTION;
                        else if (reparseDataBuffer.ReparseTag == IO_REPARSE_TAG_SYMLINK)
                            return (fileInfo.Value.FileAttributes & W32FileAttributes.Directory) > 0 ? LinkType.SYMLINKD : LinkType.SYMLINK;
                    }
                    finally { Marshal.FreeHGlobal(outBuffer); }
                }
                else if (fileInfo.Value.NumberOfLinks > 1)
                    return LinkType.HardLink;
                return LinkType.None;
            }
        }

        public static LinkType GetLinkType(string LinkPointPath, bool CheckExists = true, bool IgnorExceptions = false)
        {
            if (CheckExists && !Path.Exists(LinkPointPath))
                return LinkType.None;
            int error;
            LinkType result = GetFileLinkType(LinkPointPath, IgnorExceptions, &error);
            if (!IgnorExceptions && result == LinkType.None)
                throw new GetListTypeException($"获取连接类型异常：{error}", Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error()));
            return result;
        }



        public static void CreateLink(string LinkPoint, string LinkTargetPath, bool ForceCreateDirectory = false, bool EnableOverwrite = false) =>
            CreateLink(GetLinkType(LinkPoint, true), LinkPoint, LinkTargetPath, ForceCreateDirectory, EnableOverwrite);
        public static void CreateLink(LinkType linkType, string LinkPoint, string LinkTargetPath, bool ForceCreateDirectory = false, bool EnableOverwrite = false) =>
            CreateLink(linkType, PathInfo.GetPathType(LinkTargetPath), LinkPoint, LinkTargetPath, ForceCreateDirectory, EnableOverwrite);
        public static void CreateLink(LinkType linkType, PathType TargetPathType, string LinkPoint, string LinkTargetPath, bool ForceCreateDirectory = false, bool EnableOverwrite = false) =>
            CreateLink(linkType, TargetPathType, PathInfo.GetPathType(LinkPoint), LinkPoint, LinkTargetPath, ForceCreateDirectory, EnableOverwrite);
        public static void CreateLink(LinkType linkType, PathType TargetPathType, PathType LinkPointPathType, string LinkPoint, string LinkTargetPath, bool ForceCreateDirectory = false, bool EnableOverwrite = false)
        {
            if (linkType == LinkType.None)
                throw new ArgumentException($"未指定连接类型");
            if (linkType == LinkType.SingleFile)
                throw new ArgumentException($"不支持的连接类型");

            if (TargetPathType == PathType.NotExistType)
                throw new PathNotFoundException($"目标路径或文件 [{LinkTargetPath}] 不存在");

            string? ParentPath = Path.GetDirectoryName(LinkPoint);
            if (!Directory.Exists(ParentPath))
            {
                if (ForceCreateDirectory && Path.Exists(Path.GetPathRoot(ParentPath)))
                    Directory.CreateDirectory(ParentPath!);
                throw new DirectoryNotFoundException($"父路径[{ParentPath}]不存在");
            }

            if (LinkPointPathType != PathType.NotExistType)
            {
                if (EnableOverwrite)
                {
                    if (LinkPointPathType == PathType.FileType)
                        FileSystem.DeleteFile(LinkPoint, UIOption.OnlyErrorDialogs, RecycleOption.SendToRecycleBin);
                    else FileSystem.DeleteDirectory(LinkPoint, UIOption.OnlyErrorDialogs, RecycleOption.SendToRecycleBin);
                }
                else
                    throw new IOException($"当前位置 [{LinkPoint}] 已存在");
            }

            if (linkType == LinkType.SYMLINK || linkType == LinkType.SYMLINKD)
            {
                if (TargetPathType == PathType.FileType)
                    File.CreateSymbolicLink(LinkPoint, LinkTargetPath);
                else Directory.CreateSymbolicLink(LinkPoint, LinkTargetPath);
            }
            else if (linkType == LinkType.JUNCTION)
                CreateJunctionLink(LinkPoint, LinkTargetPath);
            else if (linkType == LinkType.HardLink)
                CreateHardLink(LinkPoint, LinkTargetPath);
        }

    }
}
